﻿using HRX.PayCenter.ApplicationCore;
using HRX.PayCenter.ApplicationCore.Interfaces;
using HRX.PayCenter.Common;
using HRX.PayCenter.Contract;
using HRX.PayCenter.Infrastructure;
using HRX.PayCenter.Web.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace HRX.PayCenter.Web.Filters
{
    /// <summary>
    /// 验证签名
    /// 1、sign=md5(k1=v1&k2=k2&...&kn=vn+密钥)
    /// 2、签名类型32位小写
    /// 3、k转小写,按有序字典排列
    /// 4、v为空值时, 省略kv项
    /// </summary>
    public class SignValidateAttribute : Attribute, IFilterFactory
    {
        /// <summary>
        /// 
        /// </summary>
        public bool IsReusable { get; set; } = true;

        /// <summary>
        /// 是否使用
        /// </summary>
        public bool IsUse { get; set; } = true;

        /// <summary>
        /// Implement IFilterFactory
        /// </summary>
        /// <param name="serviceProvider"></param>
        /// <returns></returns>
        public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
        {
            var impl = new SignValidateAttributeImpl(serviceProvider);
            impl.IsUse = IsUse;
            return impl;
        }

        /// <summary>
        /// 签名验证具体实现
        /// </summary>
        private class SignValidateAttributeImpl : IActionFilter
        {
            private readonly IAppLogger<SignValidateAttributeImpl> _appLogger;
            private readonly PayCenterConfig _payCenterConfig;

            /// <summary>
            /// 是否使用
            /// </summary>
            public bool IsUse { get; set; }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="serviceProvider"></param>
            public SignValidateAttributeImpl(IServiceProvider serviceProvider)
            {
                _appLogger = serviceProvider.GetRequiredService<IAppLogger<SignValidateAttributeImpl>>();
                _payCenterConfig = serviceProvider.GetRequiredService<IOptions<PayCenterConfigWarpper>>().Value?.PayCenterConfig;
            }

            /// <summary>
            /// 执行前
            /// </summary>
            /// <param name="context"></param>
            public void OnActionExecuting(ActionExecutingContext context)
            {
                if (!IsUse)
                {
                    return;
                }

                IDictionary<string, string> dictRequest = null;
                if (context.HttpContext.Request.Method == "GET")
                {
                    dictRequest = context.HttpContext.Request.Query.QueryDict();
                } //GET from query
                else
                {
                    var inputStream = context.HttpContext.Request.Body;
                    try
                    {
                        dictRequest = inputStream.BodyStreamDict();
                    }
                    catch (Exception ex)
                    {
                        _appLogger.LogError(ex, $"【签名认证失败】获取body内容失败");
                        context.Result = SignFail("无法正常获取验签内容");
                    }
                    finally
                    {
                        if (inputStream != null)
                        {
                            inputStream.Dispose();
                        }
                    }
                } //POST from body

                if (dictRequest.IsNullOrEmpty())
                {
                    context.Result = SignFail("请求参数为空");
                    return;
                }

                if (!dictRequest.ContainsKey(CommonSecurity.SIGN))
                {
                    context.Result = SignFail("没有sign参数");
                    return;
                }

                var signStr = CommonSecurity.GetSignContent(dictRequest);

                var sign = dictRequest[CommonSecurity.SIGN];
                var mySign = CommonSecurity.SignWithMD5(signStr + _payCenterConfig.SecurityKey);
                if (!CommonSecurity.SignVaild(sign, mySign))
                {
                    _appLogger.LogInformation(null, $"【签名失败】真实签名{mySign},请求签名{sign} 签名字符串{signStr}");
                    context.Result = SignFail("签名验证失败");
                } //验证签名
            }

            /// <summary>
            /// 签名失败
            /// </summary>
            /// <param name="message"></param>
            /// <returns></returns>
            private ObjectResult SignFail(string message)
            {
                return new ObjectResult(ResultSet<string>.Fail((int)ResultCode.签名验证错误, message));
            }

            /// <summary>
            /// 执行后
            /// </summary>
            /// <param name="context"></param>
            public void OnActionExecuted(ActionExecutedContext context)
            {
            }
        }
    }


    //public class SignValidateAttribute : TypeFilterAttribute
    //{
    //    public int CustomField { get; set; }

    //    /// <summary>
    //    /// 构造
    //    /// </summary>
    //    public SignValidateAttribute() : base(typeof(SignValidateAttributeImpl))
    //    {

    //    }
    //}

}
